import { Injectable, CanActivate, ExecutionContext, UnauthorizedException, ForbiddenException } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { JwtService } from '@nestjs/jwt'
import { Request } from 'express'
import { LoggerService } from '../logger/logger.service'
import {
  IS_PUBLIC_KEY,
  ROLES_KEY,
  PERMISSIONS_KEY,
  OWNER_ONLY_KEY,
  UserRole,
  Permission,
  ROLE_PERMISSIONS
} from '../decorators/auth.decorator'
import { UserInfo } from '../decorators/user.decorator'

/**
 * JWT认证守卫
 * 用于验证JWT令牌和用户身份
 */
@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(
    private readonly jwtService: JwtService,
    private readonly reflector: Reflector,
    private readonly logger: LoggerService
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 检查是否为公开接口
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass()
    ])

    if (isPublic) {
      return true
    }

    const request = context.switchToHttp().getRequest<Request>()
    const token = this.extractTokenFromHeader(request)

    if (!token) {
      this.logger.warn(
        '访问受保护资源时未提供JWT令牌',
        JSON.stringify({
          url: request.url,
          method: request.method,
          ip: request.ip
        })
      )
      throw new UnauthorizedException('访问令牌缺失')
    }

    try {
      // 验证JWT令牌
      const payload = await this.jwtService.verifyAsync(token, {
        secret: process.env.JWT_SECRET
      })

      // 检查令牌是否过期
      if (payload.exp && Date.now() >= payload.exp * 1000) {
        throw new UnauthorizedException('访问令牌已过期')
      }

      // 构建用户信息
      const user: UserInfo = {
        id: payload.sub,
        username: payload.username,
        email: payload.email,
        role: payload.role,
        permissions: payload.permissions || [],
        avatar: payload.avatar,
        nickname: payload.nickname,
        status: payload.status,
        createdAt: new Date(payload.createdAt),
        updatedAt: new Date(payload.updatedAt)
      }

      // 检查用户状态
      if (user.status !== 'active') {
        this.logger.warn(
          '非活跃用户尝试访问',
          JSON.stringify({
            userId: user.id,
            username: user.username,
            status: user.status,
            url: request.url
          })
        )
        throw new UnauthorizedException('用户账户已被禁用')
      }

      // 将用户信息附加到请求对象
      request.user = user

      this.logger.debug('JWT认证成功', {
        userId: user.id,
        username: user.username,
        role: user.role,
        url: request.url,
        method: request.method
      })

      return true
    } catch (error) {
      this.logger.error(
        'JWT认证失败',
        error.stack,
        JSON.stringify({
          error: error.message,
          token: token.substring(0, 20) + '...',
          url: request.url,
          method: request.method,
          ip: request.ip
        })
      )

      if (error.name === 'TokenExpiredError') {
        throw new UnauthorizedException('访问令牌已过期')
      } else if (error.name === 'JsonWebTokenError') {
        throw new UnauthorizedException('无效的访问令牌')
      } else if (error.name === 'NotBeforeError') {
        throw new UnauthorizedException('访问令牌尚未生效')
      } else {
        throw new UnauthorizedException('令牌验证失败')
      }
    }
  }

  /**
   * 从请求头中提取JWT令牌
   */
  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? []
    return type === 'Bearer' ? token : undefined
  }
}

/**
 * 角色守卫
 * 用于验证用户角色权限
 */
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly logger: LoggerService
  ) {}

  canActivate(context: ExecutionContext): boolean {
    // 获取所需角色
    const requiredRoles = this.reflector.getAllAndOverride<UserRole[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass()
    ])

    if (!requiredRoles || requiredRoles.length === 0) {
      return true
    }

    const request = context.switchToHttp().getRequest<Request>()
    const user = request.user as UserInfo

    if (!user) {
      this.logger.warn(
        '角色验证时用户信息缺失',
        JSON.stringify({
          url: request.url,
          method: request.method,
          requiredRoles
        })
      )
      throw new UnauthorizedException('用户信息缺失')
    }

    const hasRole = requiredRoles.includes(user.role as UserRole)

    if (!hasRole) {
      this.logger.warn(
        '用户角色权限不足',
        JSON.stringify({
          userId: user.id,
          username: user.username,
          userRole: user.role,
          requiredRoles,
          url: request.url,
          method: request.method
        })
      )
      throw new ForbiddenException('角色权限不足')
    }

    this.logger.debug('角色验证通过', {
      userId: user.id,
      username: user.username,
      userRole: user.role,
      requiredRoles,
      url: request.url
    })

    return true
  }
}

/**
 * 权限守卫
 * 用于验证用户具体权限
 */
@Injectable()
export class PermissionsGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly logger: LoggerService
  ) {}

  canActivate(context: ExecutionContext): boolean {
    // 获取所需权限
    const requiredPermissions = this.reflector.getAllAndOverride<Permission[]>(PERMISSIONS_KEY, [
      context.getHandler(),
      context.getClass()
    ])

    if (!requiredPermissions || requiredPermissions.length === 0) {
      return true
    }

    const request = context.switchToHttp().getRequest<Request>()
    const user = request.user as UserInfo

    if (!user) {
      this.logger.warn(
        '权限验证时用户信息缺失',
        JSON.stringify({
          url: request.url,
          method: request.method,
          requiredPermissions
        })
      )
      throw new UnauthorizedException('用户信息缺失')
    }

    // 获取用户角色对应的权限
    const rolePermissions = ROLE_PERMISSIONS[user.role as UserRole] || []

    // 合并角色权限和用户特定权限
    const userPermissions = [...rolePermissions, ...(user.permissions || [])]

    // 检查是否拥有所有必需权限
    const hasAllPermissions = requiredPermissions.every(permission => userPermissions.includes(permission))

    if (!hasAllPermissions) {
      const missingPermissions = requiredPermissions.filter(permission => !userPermissions.includes(permission))

      this.logger.warn(
        '用户权限不足',
        JSON.stringify({
          userId: user.id,
          username: user.username,
          userRole: user.role,
          userPermissions,
          requiredPermissions,
          missingPermissions,
          url: request.url,
          method: request.method
        })
      )

      throw new ForbiddenException(`权限不足，缺少权限: ${missingPermissions.join(', ')}`)
    }

    this.logger.debug('权限验证通过', {
      userId: user.id,
      username: user.username,
      userRole: user.role,
      requiredPermissions,
      url: request.url
    })

    return true
  }
}

/**
 * 资源所有者守卫
 * 用于验证用户是否为资源所有者
 */
@Injectable()
export class OwnerGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly logger: LoggerService
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 检查是否需要所有者验证
    const ownerOnly = this.reflector.getAllAndOverride<boolean>(OWNER_ONLY_KEY, [
      context.getHandler(),
      context.getClass()
    ])

    if (!ownerOnly) {
      return true
    }

    const request = context.switchToHttp().getRequest<Request>()
    const user = request.user as UserInfo

    if (!user) {
      this.logger.warn(
        '所有者验证时用户信息缺失',
        JSON.stringify({
          url: request.url,
          method: request.method
        })
      )
      throw new UnauthorizedException('用户信息缺失')
    }

    // 超级管理员和管理员可以访问所有资源
    if (user.role === UserRole.SUPER_ADMIN || user.role === UserRole.ADMIN) {
      this.logger.debug('管理员权限通过所有者验证', {
        userId: user.id,
        username: user.username,
        userRole: user.role,
        url: request.url
      })
      return true
    }

    // 从路径参数中获取资源ID
    const resourceId = request.params.id
    if (!resourceId) {
      this.logger.warn(
        '所有者验证时资源ID缺失',
        JSON.stringify({
          userId: user.id,
          url: request.url,
          params: request.params
        })
      )
      throw new ForbiddenException('资源ID缺失')
    }

    // 这里需要根据具体业务逻辑来验证所有者关系
    // 由于这是通用守卫，具体的所有者验证逻辑应该在服务层实现
    // 这里只是示例，实际使用时需要注入相应的服务来验证所有者关系

    this.logger.debug('所有者验证需要在业务层实现', {
      userId: user.id,
      resourceId,
      url: request.url
    })

    // 默认允许通过，具体验证在业务层进行
    return true
  }
}

/**
 * 组合认证守卫
 * 结合JWT认证、角色验证和权限验证
 */
@Injectable()
export class CombinedAuthGuard implements CanActivate {
  constructor(
    private readonly jwtAuthGuard: JwtAuthGuard,
    private readonly rolesGuard: RolesGuard,
    private readonly permissionsGuard: PermissionsGuard,
    private readonly ownerGuard: OwnerGuard,
    private readonly logger: LoggerService
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    try {
      // 1. JWT认证
      const jwtResult = await this.jwtAuthGuard.canActivate(context)
      if (!jwtResult) {
        return false
      }

      // 2. 角色验证
      const roleResult = this.rolesGuard.canActivate(context)
      if (!roleResult) {
        return false
      }

      // 3. 权限验证
      const permissionResult = this.permissionsGuard.canActivate(context)
      if (!permissionResult) {
        return false
      }

      // 4. 所有者验证
      const ownerResult = await this.ownerGuard.canActivate(context)
      if (!ownerResult) {
        return false
      }

      return true
    } catch (error) {
      this.logger.error(
        '组合认证守卫验证失败',
        error.stack,
        JSON.stringify({
          error: error.message
        })
      )
      throw error
    }
  }
}

/**
 * API密钥守卫
 * 用于验证API密钥访问
 */
@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private readonly logger: LoggerService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest<Request>()
    const apiKey = request.headers['x-api-key'] as string

    if (!apiKey) {
      this.logger.warn(
        'API密钥缺失',
        JSON.stringify({
          url: request.url,
          method: request.method,
          ip: request.ip
        })
      )
      throw new UnauthorizedException('API密钥缺失')
    }

    // 验证API密钥（这里应该从数据库或配置中获取有效的API密钥）
    const validApiKeys = process.env.VALID_API_KEYS?.split(',') || []

    if (!validApiKeys.includes(apiKey)) {
      this.logger.warn(
        'API密钥验证失败',
        JSON.stringify({
          apiKey: apiKey.substring(0, 8) + '...',
          url: request.url,
          method: request.method,
          ip: request.ip
        })
      )
      throw new UnauthorizedException('无效的API密钥')
    }

    this.logger.debug('API密钥验证通过', {
      apiKey: apiKey.substring(0, 8) + '...',
      url: request.url,
      method: request.method
    })

    return true
  }
}

/**
 * 速率限制守卫
 * 用于限制请求频率
 */
@Injectable()
export class RateLimitGuard implements CanActivate {
  private readonly requests = new Map<string, { count: number; resetTime: number }>()
  private readonly maxRequests = parseInt(process.env.RATE_LIMIT_MAX || '100')
  private readonly windowMs = parseInt(process.env.RATE_LIMIT_WINDOW || '900000') // 15分钟

  constructor(private readonly logger: LoggerService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest<Request>()
    const key = this.getKey(request)
    const now = Date.now()

    const record = this.requests.get(key)

    if (!record || now > record.resetTime) {
      // 新的时间窗口
      this.requests.set(key, {
        count: 1,
        resetTime: now + this.windowMs
      })
      return true
    }

    if (record.count >= this.maxRequests) {
      this.logger.warn(
        '请求频率超限',
        JSON.stringify({
          key,
          count: record.count,
          maxRequests: this.maxRequests,
          url: request.url,
          method: request.method,
          ip: request.ip
        })
      )
      throw new ForbiddenException('请求频率超限，请稍后再试')
    }

    record.count++
    return true
  }

  private getKey(request: Request): string {
    const user = request.user as UserInfo
    // 如果用户已登录，使用用户ID；否则使用IP地址
    return user?.id || request.ip || 'unknown'
  }

  /**
   * 清理过期记录
   */
  private cleanup(): void {
    const now = Date.now()
    for (const [key, record] of this.requests.entries()) {
      if (now > record.resetTime) {
        this.requests.delete(key)
      }
    }
  }
}
